﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using DecisionFramework.Logging;

namespace DecisionFramework.Logging.Internal
{
	/// <summary>
	/// Provides an implementation of ILogChannel that logs 
    /// events to either a pre Windows 7 or post Windows 7 
    /// event logs.
	/// </summary>
	/// <remarks>  
	/// Author: Greg Cowin
	/// </remarks> 
	public class LogChannel : ILogChannel
	{
        /// <summary>
        /// Whether the channel has failed over to the Application
        /// event log.
        /// </summary>
		private static bool HasSwitched = false;

        /// <summary>
        /// The channel associated with the log channel: Admin, Operational, Analytics, Debug.
        /// </summary>
		public IChannel Channel {get; internal set; }

        /// <summary>
        /// The name of the component that will be logged such as class name
        /// or component.
        /// </summary>
		public string Component{get; internal set; }

        /// <summary>
        /// Write the given app event to the log.
        /// </summary>
        /// <param name="message">The what happened to log.</param>
		public void WriteAppEvent(string whatHappened)
		{
			WriteAppEvent(whatHappened, Severity.Info);
		}

        /// <summary>
        /// Write the given app event with the given event type such as Critical, Error, Info, or Verbose.
        /// </summary>
        /// <param name="message">The what happened to log.</param>
        /// <param name="eventType">The event type such as Critical, Error, Info, or Verbose.</param>
		public void WriteAppEvent(string whatHappened, Severity eventType)
		{
			AppEvent logEvent = new AppEvent();
			logEvent.WhatHappened = whatHappened;
			logEvent.Severity = eventType;
			WriteLogEvent(Channel.ContainerOrTechnology, logEvent);
		}

        /// <summary>
        /// Write the given app event with exception. The severity will be Error.
        /// </summary>
        /// <param name="message">The what happened to log.</param>
        /// <param name="e">The exception to log.</param>
		public void WriteAppEvent(string whatHappened, Exception e)
		{
			AppEvent logEvent = new AppEvent();
			logEvent.WhatHappened = whatHappened;
			logEvent.Severity = Severity.Error;
			logEvent.Exception = e;

			WriteLogEvent(Channel.ContainerOrTechnology, logEvent);
		}

        /// <summary>
        /// Write the given app event.
        /// </summary>
        /// <param name="eventLog">The app event to log.</param>
		public void WriteAppEvent(AppEvent appEvent)
		{
			WriteLogEvent(Channel.ContainerOrTechnology, appEvent);
		}

        /// <summary>
        /// Synchronized write of the given app event.
        /// </summary>
        /// <param name="eventLog">The app event to log.</param>
        [MethodImpl(MethodImplOptions.Synchronized)]
		private void WriteLogEvent(string containerName, AppEvent appEvent)
		{
			string source = Channel.CompanyName + "-" + containerName + "-" + Component + "." + Channel.ChannelType.ToString();
			string message = source + "\n\n" + appEvent.ToString();
			EventLogEntryType type = EventLogEntryType.Information;

			type = DetermineSeverity(appEvent, type);
			
			try
			{
				EventLog log = new EventLog(Channel.CompanyName, Environment.MachineName, Channel.CompanyName);
				log.WriteEntry(message, type);
			}
			catch (Exception e)
			{
				EventLog log = new EventLog("Application", Environment.MachineName, "Application");
				log.WriteEntry(message, type);
				
				if (!HasSwitched)
				{
					HasSwitched = true;
					EventLog nested = new EventLog("Application", Environment.MachineName, Channel.CompanyName);
					AppEvent switchOverProblem = new AppEvent {
						WhatHappened="Event log had to switch to Application due to this issue",
						WhyDidItHappen="Exception: " + e.ToString(),
						Severity = Severity.Info,
						ExceptionText = "Exception: " + e.ToString() + " " + e.StackTrace,
					};
					log.WriteEntry(switchOverProblem.ToString(), EventLogEntryType.Warning);
				}
			}
		}
 
        /// <summary>
        /// Whether to suppress the severity. That is, if true
        /// then the severity will be informational only.
        /// </summary>
        /// <returns></returns>
		private static bool ShouldSuppressSeverity()
		{
            return LoggingAppSettings.IsSeveritySuppressed;
		}

        /// <summary>
        /// Determine the coresponding EventLog Entry Type.
        /// </summary>
        /// <param name="appEvent">The app event to log.</param>
        /// <param name="type">The event log entry type.</param>
        /// <returns>The EventLogEntryType associated with the app event.</returns>
		private static EventLogEntryType DetermineSeverity(AppEvent appEvent, EventLogEntryType type)
		{
			if (ShouldSuppressSeverity())
				type = EventLogEntryType.Information;
			else
			{
				if (appEvent.Severity == Severity.Error) type = EventLogEntryType.Error;
				else if (appEvent.Severity == Severity.Warning) type = EventLogEntryType.Warning;
				else if (appEvent.Severity == Severity.Verbose) type = EventLogEntryType.Information;
			}
			return type;
		}
	}
}
